org 0x7c00
jmp short start ;jmp 一个字节 短跳转1个字节 剩下一个字节用nop 跳转
nop
;定义栈顶基地址
define:
 BaseOfStack equ 0x7c00
 RootEntryOffset equ 19
 RootEntryLength equ 14
 BaseOfLoader equ 0x9000 ;加载loader内存地址
 FatEntryLength equ 9 ;9个扇区

 FatEntryOffset equ 1 ;起始扇区号1

 EntryItemLength equ 32

;定义fat32文件头部
header:
  BS_OEMNAME db "D.T.Soft"
  BPB_BytsPersec dw 512
  BPB_SecPerClus db 1
  BPB_RsvdSecCnt dw 1
  BPB_NumFATS    db 2
  BPB_RootEntCnt dw 224
  BPB_TotSec16   dw 2880
  BPB_Media      db 0xF0
  BPB_FATSz16    dw 9
  BPB_SecPerTrk  dw 18
  BPB_NumHeads   dw 2
  BPB_HiddSec    dd 0
  BPB_TotSec32   dd 0
  BS_DrvNum      db 0
  BS_Reserved1   db 0
  BS_BootSig     db 0x29
  BS_VolID       dd 0
  BS_VOlLab      db "D.T.OS-0.01"
  BS_FileSysType db "FAT12"

start:;初始化寄存器
  mov ax, cs
  mov ss, ax
  mov ds, ax
  mov es, ax
  mov sp, BaseOfStack 

 ;加载根目录区
  mov ax,RootEntryOffset
  mov cx,RootEntryLength
  mov bx,Buf
  call ReadSector

  mov si,DEST
  mov cx,TarLen
  mov dx,0
  call FindEntry
  cmp dx,0
  jz output
  
  mov si,bx;起始地址放入si
  mov di,EntryItem
  mov cx,EntryItemLength
  call MemCpy

  mov ax,FatEntryLength
  mov cx,[BPB_BytsPersec] ;每一个扇区的大小
  mul cx

  mov bx,BaseOfLoader
  sub bx,ax ;bx fat表在内存中起始位置
  mov ax,FatEntryOffset
  mov cx, FatEntryLength
  call ReadSector ;Fat表加载进入内存


  ;dir structure start first cluster
  mov dx,[EntryItem+0x1A] ;目标文件起始簇0x1A
  mov si,BaseOfLoader

loading:
  mov ax,dx
  add ax,31
  mov cx,1

  push dx
  push bx
  mov bx,si
  call ReadSector
  pop bx
  pop dx

  mov cx,dx
  call FatVec

  cmp dx,0xFF7

  jnb BaseOfLoader
  
  ;read next sector
  add si,512
  jmp loading


output:
  mov bp, MsgStr
  mov cx,MsgLen
 ; mov bp,BaseOfLoader
 ; mov cx,[EntryItem+0x1C];dir length
  call Print
  
last:
  hlt
  jmp last

;fat表项一个表项占用1.5个字节 使用3个字节表示两个表项
;fatvec[0]---> 起始字节 i = 0 
;fatvec[1]---> 起始字节 i = 0
;fatvec[2]---> 起始字节 i = 3
;fatvec[3]---> 起始字节 i = 3
;fatvec[j]---> i = j/2*3
;11010010 11101001 10101101  
;j = 0 FatVec[0] = (00001001 <<8 ) | 11010010 = 1001 0000 0000 | 1101 0010 = 1001 1101 0010 

;j = 1 FatVec[1] = (1010 1101 0000)|(0000 1110 1001)&0x0F= (1010 1101 0000)| 0000 0000 1001
;i=j/2*3
;j = 奇数时候   FatVec[j+1] = (Fat[i+2] << 4) | ((Fat[i+1] >> 4)&0x0F) ;
;j = 偶数的时候 FatVec[j] = (Fat[i+1]&0x0F)<<8) | Fat[i];取高位字节 低四位 右移8位到高位 0000 xxxx 第i位8个字节正好 1.5
;16位乘法操作 被乘数放到AL寄存器 乘数放到通用寄存器或者内存单元(8位) 相乘的结果放入Ax寄存器中
;cx--->Fat表项的下标j fatvec[j]
;bx--->fat table address
;return dx----->fat[index]
FatVec:
  mov ax,cx
  mov cl,2
  div cl
  ;ax 保存余数和商 查看奇偶
  push ax
  mov ah,0;al商 ah清0 
  mov cx,3;被乘数
  mul cx;al*3
  mov cx,ax;保存乘法结果 i 求 起始字节 i
  pop ax
  cmp ah,0; ah 保存余数  j是奇数还是偶数
  jz even
  jmp odd

even:
 ;11010010 11101001 10101101  
 ;FatVec[j] = (Fat[i+1]&0x0F)<<8) | Fat[i]; 
 mov dx,cx;dx = cx = i
 add dx,1;i+1
 add dx,bx;fat[i+1] 的真实地址 dx = fat+i+1的地址
 mov bp,dx;bp = fat+i+1 地址 
 mov dl,byte [bp];dl = fat+i+1的内容xxxxxxxx 11101001
 and dl,0x0F;dl & 0x0f xxxx xxxx 00001001
 shl dx,8; 左边移动8位 0000 1001 0000 0000

 add cx,bx;cx = fat起始地址
 mov bp,cx;bp = fat 起始地址 [bp]=11010010
 or dl,byte [bp]; 这里应该是dx? dl two byte  (shl dx,8)  in dl
 jmp return
odd:
  ;11010010 11101001 10101101  
  ;FatVec[j+1] = (Fat[i+2] << 4) | ((Fat[i+1] >> 4)&0x0F);
  mov dx,cx;dx =cx=i
  add dx,2;dx = i+2
  add dx,bx;dx = fat[i+2]的起始地址
  mov bp,dx
  mov dl,byte [bp];xxxx xxxx 1010 1101
  mov dh,0 ;0000 0000 1010 1101
  shl dx,4 ;0000 1010 1101 0000

  add cx,1 ;cx = i+1
  add cx,bx ;cx = fat[i+1]
  mov bp,cx ;bp = fat[i+1]
  mov cl,byte [bp] ;cx = xxxx xxxx  1110 1001 
  shr cl,4 ;xxxx xxxx xxxx 1110
  and cl,0x0F
  mov ch,0
  or dx,cx ;dx fat[index]

return:
  ret


;ds:si ---->source
;es:di ---->destination
;cx   ------>length
MemCpy:
  cmp si,di ;比较源目标地址和目标地址的大小关系
  ja btoe ;si > di jump   (ja >) (jna <=)  (jb <) (jnb >=)

  ;从后往前拷贝 location si and di to end   (end big) (begin small)
  add si,cx
  add di,cx
  dec si ;index start form zero
  dec di ;
  jmp etob

btoe:;头向尾部拷贝 begin to end
  cmp cx,0
  jz done;
  mov al,[si]
  mov byte [di],al
  inc si
  inc di
  dec cx
  jmp btoe
etob:;尾部向头部拷贝 end to begin
  cmp cx,0
  jz done
  mov al,[si]
  mov byte [di],al
  dec si
  dec di
  dec cx
  jmp etob

done:
  ret


;es:bx---->root entry offset addr
;ds:si----->target string
;cx ---> target len
;return:
;dx != 0 ?exisit:noexisit
;exist--->bx is the target entry
FindEntry:
  push cx; 目标字符串长度 入栈
  ;dx 是返回值 不需要保存

  
  mov dx,[BPB_RootEntCnt] ;最大查找次数
  mov bp,sp ;访问栈顶 为了访问目标字符串 的长度
find:
  cmp dx,0
  jz noexist

  mov di,bx; 根目录起始地址
  mov cx,[bp]
  ;si 再调用FindEntry已经指定过了
  push si
  call MemCmp 
  pop si
  cmp cx,0
  jz exist
  add bx,32 ;查找下一项 如果每找到
  dec dx;项数-1
  jmp find

noexist:
exist:
  pop cx
 
  ret

;ds:si -->source
;es:di -->destination
;cx --->length
;return:
; cx==0 ?equal : noequal
MemCmp:
  ;cx 为什么没有 保存堆栈 是因为cx 作为返回值使用 cx == 0 就是相等 cx != 0就是不相等
compare:
  cmp cx,0
  jz equal
  mov al,[si]
  cmp al,byte [di];指定取一个字节
  jz goon
  jmp noequal

goon:
  inc si
  inc di
  dec cx
  jmp compare

equal:
noequal:
  ret




;指定函数调用参数 传递es:bp 目标字符串的地址 和目标字符串的长度cx
Print:
  mov dx,0;location screen to left up corner
  mov ax, 0x1301 ;指定打印参数
  mov bx, 0x0007
  int 0x10
  ret

;重置软区函数 no parameter
ResetFloppy:
  mov ah,0x00
  mov dl,[BS_DrvNum]
  int 0x13
  ret

;读取软区数据
;ax ---->logic sector number
;cx ---->number of sector
;es:bx -----target address
ReadSector:
  call ResetFloppy
  
  push bx;?目标地址的值 放入栈里面
  push cx
  mov bl,[BPB_SecPerTrk];这个地方改变bx 寄存器的值 目标内存地址被改变了 所以没有打印东西? 所以上面需要push一下
  div bl;商在al 余数在ah
  mov cl,ah
  add cl,1 ;扇区号
  mov ch,al
  shr ch,1 ;柱面号
  mov dh,al
  and dh,1;磁头号
  mov dl,[BS_DrvNum];驱动器号

  pop ax ;cx 给ax 扇区的数量
  pop bx;取出目标地址的值

  ;从磁盘将数据读取ES:BX指向的内存中
  mov ah, 0x02
read:
  int 0x13
  jc read ;出错跳转重新读取

  ret

MsgStr db "gac, DTOS!"
MsgLen equ ($-MsgStr) ;用当前地址-这个地址 = 字符串的长度

DEST db "LOADER     "
TarLen equ ($-DEST)

EntryItem times EntryItemLength db 0x00

Buf:
  times 510-($-$$) db 0x00
  db 0x55, 0xaa
